package com.ming_za.odupdaterserver.core.service.res;

import com.ming_za.odupdaterserver.api.LabelManager;
import com.ming_za.odupdaterserver.core.exception.BusinessException;
import com.ming_za.odupdaterserver.core.exception.ROEnum;
import com.ming_za.odupdaterserver.core.service.ReturnBusinessPackage;
import com.ming_za.odupdaterserver.core.service.res.cache.ResLabelCacheOptions;
import com.ming_za.odupdaterserver.core.sqlite.domain.Label;
import com.ming_za.odupdaterserver.core.utils.BinaryUtils;
import com.ming_za.odupdaterserver.core.utils.cache.MapInternalCache;
import com.ming_za.odupdaterserver.core.utils.obj.ReturnPackage;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 标签管理器实现类
 * <p>详情见{@link LabelManager}</p>
 */
@Service
public class LabelManagerImpl implements LabelManager {

    private final BinaryUtils binaryUtils;

    private final MapInternalCache<Long, Label> labelCache;

    private long labelIdSum;

    public LabelManagerImpl(BinaryUtils binaryUtils, ResLabelCacheOptions resLabelCacheOptions) {
        this.binaryUtils = binaryUtils;
        this.labelCache = new MapInternalCache<>(resLabelCacheOptions);

        //保存所有id总和
        Map<Long, Label> labelMap = labelCache.getCacheData();
        for (Long key : labelMap.keySet()) {
            labelIdSum += key;
        }
    }

    @Override
    public long getLabelCompoundBit(Long labelId) {

        //默认标签直接返回
        if (labelId == 0){
            return 0;
        }

        long result = 0;

        Long parent = labelId;

        //从当前标签开始以父级向上融合
        do {
            Label label = labelCache.getValue(parent);
            if (label == null) {
                break;
            }
            result += label.getId();
            parent = label.getParent();
        }while (parent != null);

        return result;
    }

    public ReturnBusinessPackage<List<Label>> getLabelSons(long label){

        if (!binaryUtils.onlyOneBit(label)){
//            throw new BusinessException(ROEnum.RES_LABEL_NOT_ALONE);
            new ReturnBusinessPackage<>(false,ROEnum.RES_LABEL_NOT_ALONE,null);
        }

        List<Label> resultSon = new ArrayList<>();

        //遍历所有标签
        Map<Long, Label> labels = getLabels();
        for (Label l : labels.values()){
            //当列表中存在该子级或者当前是传入标签时直接跳过
            if (resultSon.contains(l) || l.getId() == label){
                continue;
            }

            //获取当前标签的所有父级进行判断
            Long parent = l.getParent();

            /*
            记录查找过的父级
            从当前标签一直获取父级网上判断
            当其中查找到父级标签等于传入标签时，将查找过的所有父级都视为传入标签的子级，保存到返回结果中
             */
            List<Label> resultCache = new ArrayList<>();
            do {
                //当标签父级与传入值相符时记录且跳出循环
                if (parent == label){
                    resultSon.addAll(resultCache);
                    break;
                }

                //当标签不符时获取该父级的父级，继续循环，直到父级为null
                Label c = labels.get(parent);
                if (c == null){
                    continue;
                }
                resultCache.add(c);
                parent = c.getParent();
            }while (parent != null);
        }

//        return resultSon;
        return new ReturnBusinessPackage<>(true,ROEnum.SUCCESS,resultSon);
    }

    @Override
    public Map<Long, Label> getLabels() {
        return labelCache.getCacheData();
    }

    @Override
    public Long addLabel(String name, Long parent) {

        long labelId = generateNewLabelId();

        //插入数据库
        Label label = new Label();
        label.setId(labelId);
        label.setParent(parent);
        label.setName(name);
        labelCache.putValue(labelId,label);

        //记录id总和
        labelIdSum += labelId;

        return labelId;
    }

    @Override
    public void delLabel(Long labelId) {
        labelCache.removeValue(labelId);

        //将所有以该标签为父级的标签父级设为0
        Map<Long, Label> cacheData = labelCache.getCacheData();
        for (Map.Entry<Long, Label> entry : cacheData.entrySet()) {
            if (entry.getValue().getParent().equals(labelId)){
                entry.getValue().setParent(0L);
                labelCache.save(entry.getKey());
            }
        }
    }

    @Override
    public ReturnBusinessPackage<Void> setLabelParent(long labelId, long parent) {
        if (labelId == parent){
//            throw new BusinessException(ROEnum.RES_LABEL_CYCLIC_EXTEND,"不能将当前标签设为父级");
            return new ReturnBusinessPackage<>(false,ROEnum.RES_LABEL_CYCLIC_EXTEND.setMsg("不能将当前标签设为父级"),null);
        }
        Label currentLabel = labelCache.getValue(labelId);
        if (currentLabel == null){
//            throw new BusinessException(ROEnum.UPDATE_NOTFOUND_LABEL);
            return new ReturnBusinessPackage<>(false,ROEnum.UPDATE_NOTFOUND_LABEL,null);
        }

        Long pc = parent;
        List<String> labels = new ArrayList<>();
        do {
            Label label = getLabel(pc);
            if (label == null){
                break;
            }
            labels.add(label.getName());
            pc = getLabel(pc).getParent();
            if (pc != null){
                if (pc == labelId){
                    StringBuilder sb = new StringBuilder(currentLabel.getName() + "->");
                    for (int i = labels.size() - 1; i >= 0; i--){
                        sb.append(labels.get(i)).append("->");
                    }
                    sb.append(currentLabel.getName());
//                    throw new BusinessException(ROEnum.RES_LABEL_CYCLIC_EXTEND,"标签循环继承：" + sb);
                    return new ReturnBusinessPackage<>(false,ROEnum.RES_LABEL_CYCLIC_EXTEND.setMsg("标签循环继承：" + sb),null);
                }
            }
        }while (pc != null && pc != 0);

        currentLabel.setParent(parent);
        labelCache.putValue(labelId,currentLabel);
        return new ReturnBusinessPackage<>(true,ROEnum.SUCCESS,null);
    }

    public Label getLabel(long labelId) {
        return labelCache.getValue(labelId);
    }

    @Override
    public Long getAllExtendsLabel(long labels) {

        Set<Long> lableSet = binaryUtils.splitLongBit(labels);

        long result = 0;
        for (long l :lableSet){
            result |= getLabelCompoundBit(l);
        }

        return result;
    }

    /**
     * 生成新的标签id
     * <p>先从id总和最高位获取，如果最高位被占则从其中找空余位</p>
     * @return 标签已满返回0
     */
    private long generateNewLabelId(){

        long result = getNextBit(labelIdSum);
        if (result == 0){
            result = getNextFreeBit(labelIdSum);
        }
        return result;
    }

    /**
     * 获取最高位下一位的数值
     * @param l 当前所有数值（id）总和
     * @return 如果传入的值已经占用了最高位，则返回0
     */
    private long getNextBit(Long l){
        //long最高位(符号位)
        long maxIndex = 0x8000000000000000L;

        //当 l 中的 maxIndex 位上已存在时返回0
        if ((l & maxIndex) == maxIndex){
            return 0;
        }
        return Long.highestOneBit(l) << 1;
    }

    /**
     * 从 l 中返回最小空余的位
     * @param l 当前所有数值（id）总和
     * @return 如果l中存在空余位则返回，否则返回0
     */
    private long getNextFreeBit(Long l){
        //long最低位
        long index = 1;

        for (int i = 0;i < 64;i++){
            if ((l & index) != index){
                return index;
            }
            index <<= 1;
        }
        return 0;
    }

}
